package com.googlecode.groovy_toy_orm.internal

import org.slf4j.Logger
import org.slf4j.LoggerFactory

import java.util.concurrent.TimeUnit


/**
 * @author: DavydovMD
 * Date: 22.03.13
 * Time: 11:00
 */




public class Retrier {
    private Closure<Boolean> tryClosure = { true };
    private vars = [:]
    private boolean doLog = true
    private String desc = "Some action"
    private static Logger logger = LoggerFactory.getLogger(Retrier.class)



    private List<Closure<Boolean>> expirationChecks = [{ false }]

    private Integer attemptsLimit = null;//By default - loop forever
    private int attemptCounter = 0
    private int pollingIntervalMillis = 100

    Retrier withAttempts(int numAttempts, int pollingInterval = pollingIntervalMillis, TimeUnit unit = TimeUnit.MILLISECONDS) {
        attemptsLimit = numAttempts
        attemptCounter = 0
        withInterval(pollingInterval, unit)
    }

    private logFailure = { failureDesc ->
        if (doLog) {
            logger.warn("Retrier: Action [$desc] failed:$failureDesc.")
        }
    }

    Retrier withLogging(String description) {
        doLog = true;
        desc = description
        this
    }

    Retrier withInterval(int interval, TimeUnit unit = TimeUnit.MILLISECONDS) {
        pollingIntervalMillis = Util.toMillis(interval, unit)
        this
    }

    Retrier withTimeout(int timeout, TimeUnit unit = TimeUnit.MILLISECONDS) {
        int timeoutMillis = Util.toMillis(timeout, unit)
        long startTime = System.currentTimeMillis()
        expirationChecks << {
            if (System.currentTimeMillis() > startTime + timeoutMillis) {
                logFailure(" Timeout of ${timeout} ${unit} exceeded")
                return true
            }
        }
        this
    }

    boolean exec(Closure tryClosure) {
        while (attemptsLimit ? attemptCounter <= attemptsLimit : true) {
            if (tryClosure()) return true
            for (Closure check in expirationChecks) {
                if (check()) return false
            }
            sleep pollingIntervalMillis
            attemptCounter++
        }
       //
        if (attemptsLimit && attemptCounter > attemptsLimit) {
            logFailure("number of attempts exceeded:${attemptCounter}")
            return false
        }   else {
            return true
        }
    }


}
